package com.kz.bot.api;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.Method;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.kz.bot.config.TradeConfig;
import com.kz.bot.constant.Side;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;

@Slf4j
@Component
@RequiredArgsConstructor
public class ApiRequest {

    private final TradeConfig config;

    /**
     * 发送请求（Hutool）
     */
    private JSONObject doRequest(Method method, String path, Map<String, Object> params, boolean auth) throws Exception {
        String requestPath = path;
        String bodyStr = "";

        if (method == Method.GET) {
            // GET 请求：拼接 URL
            if (params != null && !params.isEmpty()) {
                StringBuilder sb = new StringBuilder(path).append("?");
                params.forEach((k, v) -> sb.append(k).append("=").append(v).append("&"));
                sb.deleteCharAt(sb.length() - 1);
                requestPath = sb.toString();
            }
        } else {
            // POST/PUT 请求：params 转 JSON 放 body
            if (params != null && !params.isEmpty()) {
                bodyStr = JSON.toJSONString(params);
            }
        }

        String url = config.getBaseUrl() + requestPath;
        HttpRequest req = HttpRequest.get(url).method(method);

        // 添加认证头
        if (auth) {
            String ts = Instant.now().truncatedTo(ChronoUnit.MILLIS).toString();
            String sign = sign(ts, method, requestPath, bodyStr);
            req.header("OK-ACCESS-KEY", config.getApiKey())
                    .header("OK-ACCESS-SIGN", sign)
                    .header("OK-ACCESS-TIMESTAMP", ts)
                    .header("OK-ACCESS-PASSPHRASE", config.getApiPass())
                    .header("Content-Type", "application/json");
        }

        // 设置请求体
        if (!bodyStr.isEmpty()) {
            req.body(bodyStr);
        }

        // 执行请求
        String respBody = req.timeout(10000).execute().body();
        JSONObject jsonResult = JSON.isValid(respBody) ? JSON.parseObject(respBody) : new JSONObject();
        checkResultCode(path, params, jsonResult);
        return jsonResult;
    }

    /**
     * 检查返回值状态码
     * @param path 请求路径
     * @param params 参数
     * @param jsonResult 响应结果
     */
    public static void checkResultCode(String path, Map<String, Object> params, JSONObject jsonResult) {
        if (!"0".equals(jsonResult.getString("code"))) {
            // 状态码非0 不做后续业务逻辑处理
            log.error("响应code非0,{},{},{}", path, params, jsonResult);
            throw new RuntimeException("响应code非0");
        }
    }


    /**
     * 生成签名
     */
    private String sign(String timestamp, Method method, String requestPath, String body) throws Exception {
        String prehash = timestamp + method.name() + requestPath + (body == null ? "" : body);
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(config.getApiSecret().getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        sha256_HMAC.init(secretKey);
        byte[] hash = sha256_HMAC.doFinal(prehash.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(hash);
    }

    /**
     *
     * @param currency usdt eth btc
     * @return 币种余额
     */
    public BigDecimal queryAccountBalance(String currency) {
        try {
            if (StringUtils.isBlank(currency)) {
                throw new RuntimeException("币种名称不能为空");
            }
            Map<String, Object> params = new HashMap<>();
            params.put("ccy", currency.toUpperCase());
            JSONObject jsonResult = doRequest(Method.GET, "/api/v5/account/balance", params, true);
            log.info("查询余额 accountBalance:{},{}", currency, jsonResult);
            JSONObject jsonData = jsonResult.getJSONArray("data").getJSONObject(0);
            JSONObject jsonDetail = jsonData.getJSONArray("details").getJSONObject(0);
            // 可用余额
            String availBal = jsonDetail.getString("availBal");
            return new BigDecimal(availBal);
        } catch (Exception e) {
            return null;
        }
    }

    public JSONObject createOrder(String instId, Side side, String count, String price) throws Exception {
        // POST /api/v5/trade/order
        Map<String, Object> params = new HashMap<>();
        // 产品ID，如 BTC-USDT
        params.put("instId", instId.toUpperCase());
        // 交易模式
        // 保证金模式：isolated：逐仓 ；cross：全仓
        // 非保证金模式：cash：非保证金
        params.put("tdMode", "cash");
        // 订单方向 buy：买， sell：卖
        params.put("side", side.name());
        // 订单类型
        // market：市价单，仅适用于币币/杠杆/交割/永续
        // limit：限价单
        // post_only：只做maker单
        // fok：全部成交或立即取消
        // ioc：立即成交并取消剩余
        // optimal_limit_ioc：市价委托立即成交并取消剩余（仅适用交割、永续）
        // mmp：做市商保护(仅适用于组合保证金账户模式下的期权订单)
        // mmp_and_post_only：做市商保护且只做maker单(仅适用于组合保证金账户模式下的期权订单)
        params.put("ordType", "limit");
        // 委托数量
        params.put("sz", count);
        // 委托价格
        params.put("px", price);

        JSONObject jsonResult = doRequest(Method.POST, "/api/v5/trade/order", params, true);
        log.info("挂单 createOrder:{},{},{},{},{}", instId, side.name(), count, price, jsonResult);
        JSONObject jsonObject = jsonResult.getJSONArray("data").getJSONObject(0);
        return jsonObject;
    }

    public JSONObject cancelOrder(String instId, String ordId) throws Exception {
        // POST /api/v5/trade/cancel-order
        Map<String, Object> params = new HashMap<>();
        // 产品ID，如 BTC-USDT
        params.put("instId", instId.toUpperCase());
        params.put("ordId", ordId);

        JSONObject jsonResult = doRequest(Method.POST, "/api/v5/trade/cancel-order", params, true);
        log.info("取消委托单 cancelOrder:{},{},{}", instId, ordId, jsonResult);
        JSONObject jsonObject = jsonResult.getJSONArray("data").getJSONObject(0);
        return jsonObject;
    }

    /**
     * 查询未成交订单列表
     *
     * @return
     * @throws Exception
     */
    public List<JSONObject> pendingOrders(String instId) throws Exception {
        // GET /api/v5/trade/orders-pending
        // Map<String, Object> params = new HashMap<>();
        JSONObject jsonResult = doRequest(Method.GET, "/api/v5/trade/orders-pending", new HashMap<>(), true);
        JSONArray orderJsonArray = jsonResult.getJSONArray("data");
        List<JSONObject> orderList = new ArrayList<>();
        for (int i = 0; i < orderJsonArray.size(); i++) {
            JSONObject orderJson = orderJsonArray.getJSONObject(i);
            // 只处理现货委托
            if ("SPOT".equals(orderJson.getString("instType")) && instId.equals(orderJson.getString("instId"))) {
                orderList.add(orderJson);
            }
        }
        return orderList;
    }


    /**
     * 查询最新价格
     * @param instId 币种 ETH-USDT
     * @return 最新价格
     */
    public String queryLastPrice(String instId) throws Exception {
        // GET /api/v5/market/ticker
        Map<String, Object> params = new HashMap<>();
        params.put("instId", instId);
        JSONObject jsonResult = doRequest(Method.GET, "/api/v5/market/ticker", params, true);
        return jsonResult.getJSONArray("data").getJSONObject(0).getString("last");
    }

    /**
     * 查询订单状态
     * @param instId 币种
     * @param ordId 订单id
     * @return
     * @throws Exception
     */
    public JSONObject queryOrderStatus(String instId, String ordId) throws Exception {
        // /api/v5/trade/order
        Map<String, Object> params = new HashMap<>();
        params.put("instId", instId);
        params.put("ordId", ordId);
        JSONObject jsonResult = doRequest(Method.GET, "/api/v5/trade/order", params, true);
        return jsonResult.getJSONArray("data").getJSONObject(0);
    }

    /**
     * 子账户划转到子账户
     * @param ccy
     * @param amount
     * @return
     */
    public JSONObject assetTransfer(String ccy, String amount) throws Exception {
        /*POST /api/v5/asset/transfer
        body
        {
            "ccy":"USDT",
                "type":"4",
                "amt":"1.5",
                "from":"6",
                "to":"6",
                "subAcct":"mini"
        }*/
        Map<String, Object> params = new HashMap<>();
        params.put("ccy", ccy);
//        params.put("type", "4");
        params.put("amt", amount);
        params.put("from", "6");
        params.put("to", "18");
        JSONObject jsonResult = doRequest(Method.POST, "/api/v5/asset/transfer", params, true);
        log.info("资金划转结果:{}", jsonResult);
        return jsonResult;
    }

}
